package com.ecoolex.discount.sales.core.manager;

import com.ecoolex.discount.sales.core.dao.PaymentOrderDAO;
import com.ecoolex.discount.sales.core.service.WeiXinPayService;
import com.ecoolex.discount.sales.entity.DiscountOrder;
import com.ecoolex.discount.sales.entity.PaymentOrder;
import com.ecoolex.discount.sales.entity.PaymentOrder.Column;
import com.ecoolex.discount.sales.entity.PaymentOrderExample;
import com.ecoolex.discount.sales.entity.PaymentOrderRecord;
import com.ecoolex.discount.sales.enums.PaymentOrderRecordOperType;
import com.ecoolex.discount.sales.enums.PaymentOrderRecordResultStatus;
import com.ecoolex.discount.sales.enums.PaymentOrderStatus;
import com.ecoolex.discount.sales.pojo.QueryPaymentOrderDTO;
import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.pojo.Page;
import com.ecoolex.framework.common.pojo.Pagination;
import com.ecoolex.framework.common.pojo.Sorting;
import com.ecoolex.framework.common.pojo.Sortings;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.function.BiFunction;

import static com.ecoolex.discount.sales.entity.PaymentOrder.NOT_DELETED;

@Service
public class PaymentOrderManager {
    @Autowired
    private PaymentOrderDAO dao;


    @Autowired
    private PaymentOrderRecordManager paymentOrderRecordManager;
    @Autowired
    private WeiXinPayService weiXinPayService;

    @Autowired
    private DiscountOrderManager discountOrderManager;
    private static final String TYPE_DISCOUNT_ORDER = "DO";

    /*
    付款申请
     */
    @Transactional
    public <T> T paymentApply(DiscountOrder order, String paymentType, String createUser, BiFunction<PaymentOrder, PaymentOrderRecord, T> biFunction) {
        // 检查支付订单是否存在
        PaymentOrder record = getByBizTypeAndOrderNo(TYPE_DISCOUNT_ORDER, order.getOrderNo());
        if (Check.isNull(record)) {
            // 创建订单
            record = new PaymentOrder();
            record.setUserId(order.getUserId());
            record.setBizType(TYPE_DISCOUNT_ORDER);
            record.setOrderNo(order.getOrderNo());
            // 币种不存在时,默认人民币
            if (Check.isNull(order.getCurrencyType())) {
                record.setPrepaidCurrencyType("CNY");
            } else {
                record.setPrepaidCurrencyType(order.getCurrencyType());
            }
            record.setPrepaidPrice(order.getTotalPrice());
            record.setStatus(PaymentOrderStatus.PAY_APPLY.real());
            record.setDeleted(NOT_DELETED);
            record.setCreateUser(createUser);
            record.setCreateTime(Date.from(Instant.now()));
            record.setUpdateUser(createUser);
            record.setUpdateTime(Date.from(Instant.now()));
            dao.insert(record);
        } else {
            // 失败才能发起
            BizAssert.isTrue(PaymentOrderStatus.PAY_FAILED.equal(record.getStatus()),
                    ResultCode.BIZ.build(10),
                    "订单支付中或已支付, 无法发起支付");
            // 变更状态为支付中
            record.setStatus(PaymentOrderStatus.PAY_APPLY.real());
            record.setUpdateUser(createUser);
            record.setUpdateTime(Date.from(Instant.now()));

            PaymentOrderExample example = new PaymentOrderExample();
            example.createCriteria()
                    .andPaymentOrderIdEqualTo(record.getPaymentOrderId())
                    .andStatusEqualTo(PaymentOrderStatus.PAY_FAILED.real())
                    .andDeletedEqualTo(NOT_DELETED);

            int row = dao.updateByExampleSelective(record,
                    example,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            BizAssert.isTrue(row == 1,
                    ResultCode.BIZ.build(10),
                    "订单支付中或已支付, 无法发起支付");
        }

        // 创建一条发起记录
        PaymentOrderRecord log = paymentOrderRecordManager.create(record, paymentType, PaymentOrderRecordOperType.ORDER_APPLY);
        try {
            // 执行
            T result = biFunction.apply(record, log);
            log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
            return result;
        } catch (Exception e) {
            // 日志记录为异常
            log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            log.setErrorMessage(e.getMessage());
            // 更新支付订单为异常
            record.setStatus(PaymentOrderStatus.PAY_FAILED.real());
            record.setUpdateUser(createUser);
            record.setUpdateTime(Date.from(Instant.now()));

            PaymentOrderExample example = new PaymentOrderExample();
            example.createCriteria()
                    .andPaymentOrderIdEqualTo(record.getPaymentOrderId())
                    .andStatusEqualTo(PaymentOrderStatus.PAY_APPLY.real())
                    .andDeletedEqualTo(NOT_DELETED);

            int row = dao.updateByExampleSelective(record,
                    example,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);
            return null;
        } finally {
            // 保存记录结果
            paymentOrderRecordManager.add(log, createUser);
        }
    }

    /*
    修改支付状态并通知
     */
    @Transactional
    public void updatePayStatusAndNotify(PaymentOrder order, PaymentOrderRecord record, String updateUser) {
        if (PaymentOrderRecordResultStatus.ONGOING.equal(record.getResultStatus())) {
            // 支付中, 不处理
            return;
        }
        // 更新条件
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria()
                .andPaymentOrderIdEqualTo(order.getPaymentOrderId())
                .andDeletedEqualTo(NOT_DELETED);

        // 更新支付订单状态
        if (PaymentOrderRecordResultStatus.SUCCEED.equal(record.getResultStatus())) {
            // 如果通知成功, 则更新成功的支付信息
            order.setPaymentType(record.getPaymentType());
            order.setPaymentCurrencyType(record.getCurrencyType());
            order.setPaymentPrice(record.getTransAmount());
            //order.setRmbAmount(record.getRmbAmount());
            order.setPaymentNo(record.getTransNo());
            order.setPaymentTime(record.getTransTime());
            order.setStatus(PaymentOrderStatus.PAY_SUCCEED.real());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            int row = dao.updateByExampleSelective(order,
                    example,
                    Column.paymentType,
                    Column.paymentCurrencyType,
                    Column.paymentPrice,
                    Column.paymentNo,
                    Column.paymentTime,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            // 如果更新失败则返回
            if (row != 1) {
                return;
            }
        } else {
            // 失败则更新状态
            order.setStatus(PaymentOrderStatus.PAY_FAILED.real());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            int row = dao.updateByExampleSelective(order,
                    example,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            // 如果更新失败则返回
            if (row != 1) {
                return;
            }
        }

        // 成功则通知相关业务方
        if (order.getBizType().equals(TYPE_DISCOUNT_ORDER)) {
            discountOrderManager.payedNotify(order, updateUser);
        }
    }

    /*
    退款申请
     */
    @Transactional
    public boolean refundApply(DiscountOrder discountOrder, String updateUser) {
        PaymentOrder order = getByBizTypeAndOrderNo(TYPE_DISCOUNT_ORDER, discountOrder.getOrderNo());
        BizAssert.isTrue(Check.notNull(order), ResultCode.BIZ.build(10), "支付订单未找到, 无法发起退款");

        // 失败才能发起
        BizAssert.isTrue(PaymentOrderStatus.PAY_SUCCEED.equal(order.getStatus())
                || PaymentOrderStatus.REFUND_FAILED.equal(order.getStatus()), ResultCode.BIZ.build(11), "订单必须已支付, 才能发起退款");
        // 变更状态为发起退款
        order.setStatus(PaymentOrderStatus.REFUND_APPLY.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(Date.from(Instant.now()));

        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria()
                .andPaymentOrderIdEqualTo(order.getPaymentOrderId())
                .andStatusEqualTo(order.getStatus())
                .andDeletedEqualTo(NOT_DELETED);

        int row = dao.updateByExampleSelective(order,
                example,
                Column.status,
                Column.updateUser,
                Column.updateTime);

        BizAssert.isTrue(row == 1,
                ResultCode.BIZ.build(11),
                "订单必须已支付, 才能发起退款");

        // 创建一条发起记录
        PaymentOrderRecord log = paymentOrderRecordManager.create(order, order.getPaymentType(), PaymentOrderRecordOperType.REFUND_APPLY);
        try {
            if (WeiXinPayService.PAYMENT_TYPE.equalsIgnoreCase(order.getPaymentType())) {
                weiXinPayService.refundApply(order, log);
            }
            log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
        } catch (Exception e) {
            // 记录详情状态
            log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
            log.setErrorMessage(e.getMessage());
            // 设置退款失败
            order.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            dao.updateByPrimaryKeySelective(order,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);
        } finally {
            // 记录调用详情
            paymentOrderRecordManager.add(log, updateUser);
        }
        return PaymentOrderRecordResultStatus.SUCCEED.equal(log.getResultStatus());
    }

    @Transactional
    public void updateRefundStatusAndNotify(PaymentOrder order, PaymentOrderRecord record, String updateUser) {
        if (PaymentOrderRecordResultStatus.ONGOING.equal(record.getResultStatus())) {
            // 支付中, 不处理
            return;
        }

        // 更新条件
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria()
                .andPaymentOrderIdEqualTo(order.getPaymentOrderId())
                .andStatusEqualTo(PaymentOrderStatus.REFUND_APPLY.real())
                .andDeletedEqualTo(NOT_DELETED);

        // 更新支付订单状态
        if (PaymentOrderRecordResultStatus.SUCCEED.equal(record.getResultStatus())) {
            // 如果通知成功, 则更新成功的支付信息
            order.setRefundPrice(record.getTransAmount());
            order.setStatus(PaymentOrderStatus.REFUND_SUCCEED.real());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            int row = dao.updateByExampleSelective(order,
                    example,
                    Column.refundPrice,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            // 如果更新失败则返回
            if (row != 1) {
                return;
            }
        } else {
            // 失败则更新状态
            order.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            int row = dao.updateByExampleSelective(order,
                    example,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            // 如果更新失败则返回
            if (row != 1) {
                return;
            }
        }

        // 成功则通知相关业务方
        if (order.getBizType().equals(TYPE_DISCOUNT_ORDER)) {
            discountOrderManager.refundedNotify(order, updateUser);
        }
    }

    public PaymentOrder getByBizTypeAndOrderNo(String bizType, String orderNo) {
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria().andBizTypeEqualTo(bizType).andOrderNoEqualTo(orderNo);
        return dao.selectOneByExample(example);
    }

    public PaymentOrder getById(Integer paymentOrderId) {
        return dao.selectByPrimaryKey(paymentOrderId);
    }

    public Pagination<PaymentOrder> queryByQuery(QueryPaymentOrderDTO query, Sorting[] sorts, Page page) {
        return Pagination.create(page,
                dao.countByQuery(query),
                () -> dao.queryByQuery(query, Sortings.builder(sorts).verify(Column::valueOf).toSql(), page));
    }

    public List<PaymentOrder> queryPayApplyTimeout(Date timeout, int size) {
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria()
                .andStatusEqualTo(PaymentOrderStatus.PAY_APPLY.real())
                .andDeletedEqualTo(NOT_DELETED)
                .andUpdateTimeLessThanOrEqualTo(timeout);
        example.orderBy(Column.updateTime.asc());
        example.limit(size);
        return dao.selectByExample(example);
    }

    public List<PaymentOrder> queryRefundApplyTimeout(Date timeout, int size) {
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria().andStatusEqualTo(PaymentOrderStatus.REFUND_APPLY.real()).andDeletedEqualTo(
                PaymentOrder.NOT_DELETED).andUpdateTimeLessThanOrEqualTo(timeout);
        example.orderBy(Column.updateTime.asc());
        example.limit(size);
        return dao.selectByExample(example);
    }
    
    public void create(PaymentOrder record) {
    	dao.insert(record);
    }
    
    public void updatePaymentOrder(PaymentOrder paymentOrder,String updateUser) {
    	paymentOrder.setUpdateUser(updateUser);
        paymentOrder.setUpdateTime(Date.from(Instant.now()));
        dao.updateByPrimaryKeySelective(paymentOrder);
    }
}